Skip to content

feat: AI budgeting chat with human-in-the-loop envelope assignment#16

Merged
toolpathguy merged 1 commit into
mainfrom
feat/ai-budgeting-chat
Jun 17, 2026
Merged

feat: AI budgeting chat with human-in-the-loop envelope assignment#16
toolpathguy merged 1 commit into
mainfrom
feat/ai-budgeting-chat

Conversation

@toolpathguy

Copy link
Copy Markdown
Owner

Fixes #8

A chat assistant on the budget page that reads live budget state through tools and proposes envelope assignments/transfers for the user to approve. This is the foundation feature that establishes the shared Anthropic plumbing and the propose → approve → commit spine the CSV-import feature (#9) will reuse.

Safety invariant (load-bearing)

The chat route never writes the journal. Read tools run server-side; the proposed-action tools (assign_to_envelope, transfer_between_envelopes) are surfaced for approval, never executed. Money moves only when the client calls the existing POST /api/budget/assign|transfer endpoints after explicit user approval. Guarded by a test asserting the journal writer is called .

What's included

  • server/utils/anthropic.ts — shared Anthropic SDK client (claude-opus-4-8, adaptive thinking, effort medium). Key resolves env override → in-app stored key; the client rebuilds when the key changes (no restart). Typed billing/401/429 error messages.
  • server/utils/aiConfig.ts + GET/POST/DELETE /api/ai/config — in-app API-key configuration persisted to gitignored config/ai-config.json. The key is never logged and never returned in full (masked last-4). Settings page gains an "AI Assistant" card.
  • server/utils/aiTools.ts + server/ai/budgetInstructions.ts — read tools (get_budget/get_transactions) + proposed-action tools; cached system prompt.
  • server/api/ai/chat.post.ts — stateless human-in-the-loop tool loop (manual, 8-iteration cap); refusal/missing-key/error handling.
  • composables/useAiChat.ts + components/AiChatPanel.vue — chat UI with approve/reject cards, persistent data-egress notice, and a not-configured empty state linking to Settings.
  • server/utils/budgetReport.tsgetBudgetReport extracted from budget.get.ts so the route and the get_budget tool share one source (no duplicated accounting logic).

Notable decision

Issue #8 framed the key as env-only ("no stored secrets"). Per discussion, this PR adds an in-app key config persisted to gitignored config/ai-config.json (mirrors the config/active-journal.json precedent; acceptable for a single-user local app). Env var still takes precedence. Recorded in .kiro/specs/ai-budgeting-chat/design.md (Decision: in-app key config) and requirement R7.

Verification

  • 389 vitest tests + nuxi typecheck clean.
  • Adversarial multi-agent review of the secret handling (no key-exposure issues; two UX findings fixed).
  • Runtime-probed the /api/ai/config verbs (save takes effect with no restart) and confirmed the HITL no-write invariant.

Data egress

Chat messages + budget data are sent to the Anthropic API (the one external data flow). The chat panel shows a persistent notice; no secrets are stored beyond the gitignored API key.

Spec: .kiro/specs/ai-budgeting-chat/ (design → requirements → tasks).

🤖 Generated with Claude Code

A chat assistant on the budget page reads live budget state via tools and
PROPOSES envelope assignments/transfers for the user to approve. The chat route
never writes the journal; money moves only via the existing assign/transfer
endpoints after explicit approval (guarded by a zero-write test).

- server/utils/anthropic.ts: shared Anthropic SDK client (Opus 4.8, adaptive
  thinking). Key resolves env override -> in-app stored key; client rebuilds on
  key change (no restart). Typed billing/401/429 error messages.
- server/utils/aiConfig.ts + /api/ai/config (GET/POST/DELETE): in-app API key
  config persisted to gitignored config/ai-config.json; key never logged or
  returned in full (masked last-4). Settings page gains an AI Assistant card.
- server/utils/aiTools.ts + server/ai/budgetInstructions.ts: read tools
  (get_budget/get_transactions) + proposed-action tools; cached system prompt.
- server/api/ai/chat.post.ts: stateless HITL tool loop (manual, 8-iter cap) --
  surfaces proposals, never executes them.
- composables/useAiChat.ts + components/AiChatPanel.vue: chat UI with
  approve/reject cards, data-egress notice, not-configured empty state.
- server/utils/budgetReport.ts: getBudgetReport extracted from budget.get.ts so
  the route and the get_budget tool share one source (no duplicated accounting).

Verified: 389 vitest tests + nuxi typecheck clean; runtime-probed the config
endpoints and the HITL no-write invariant. Spec in .kiro/specs/ai-budgeting-chat/.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@toolpathguy toolpathguy merged commit 39881d4 into main Jun 17, 2026
2 checks passed
@toolpathguy toolpathguy deleted the feat/ai-budgeting-chat branch June 17, 2026 05:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: AI budgeting chat with human-in-the-loop envelope assignment

1 participant